package com.example.clock.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.clock.entity.Attend;
import com.example.clock.entity.AttendResult;
import com.example.clock.entity.AttendVo;
import com.example.clock.mapper.AttendMapper;
import com.example.clock.service.AttendService;
import com.example.clock.util.DateUtil;
import com.example.clock.util.ExcelUtil;
import com.example.clock.util.WorkDayCalculator;
import com.example.clock.vo.AttendExcel;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.One;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.text.ParseException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class AttendServicesImpl extends ServiceImpl<AttendMapper, Attend> implements AttendService {

    @Value("${clock.upStart}")
    private String upStartStr;
    @Value("${clock.upEnd}")
    private String upEndStr;
    @Value("${clock.downStart}")
    private String downStartStr;
    @Value("${clock.downEnd}")
    private String downEndStr;

    /**
     * 打卡
     *
     * @param userName 姓名
     * @param time     时间
     */
    @Override
    public String clock(String userName, LocalDateTime time) {
        try {
            //获取打卡时间的日期
            LocalDate localDate = time.toLocalDate();
            LocalTime localTime = time.toLocalTime();
            LocalTime upStart = strToTime(upStartStr);
            LocalTime upEnd = strToTime(upEndStr);
            LocalTime downStart = strToTime(downStartStr);
            LocalTime downEnd = strToTime(downEndStr);
            Integer flag = null;
            if (localTime.isAfter(upStart) && localTime.isBefore(upEnd)) {
                flag = 1;
            } else if (localTime.isAfter(downStart) && localTime.isBefore(downEnd)) {
                flag = 2;
            } else {
                return "未在打卡时间";
            }
            //查询当前日当前人是否已经打过卡了
            LambdaQueryWrapper<Attend> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(StringUtils.isNotEmpty(userName), Attend::getName, userName)
                    .eq(Attend::getCurrentDateTime, localDate)
                    .eq(Attend::getIsDelete,0);
            Attend attend = baseMapper.selectOne(wrapper);
            String msg;
            if (attend == null) {
                Attend attend1 = new Attend();
                attend1.setName(userName);
                attend1.setCurrentDateTime(localDate);
                if (flag == 1) {
                    attend1.setClockUpTime(time);
                    msg = "打卡成功";
                } else {
                    attend1.setClockDownTime(time);
                    msg = "打卡成功";
                }
                baseMapper.insert(attend1);
            } else {
                if (flag == 1) {
                    if (time.isAfter(attend.getClockUpTime())) {
                        return "已打过卡";
                    }
                    attend.setClockUpTime(time);
                    msg = "打卡成功";
                } else {
                    attend.setClockDownTime(time);
                    if (attend.getClockUpTime() != null) {
                        LocalDateTime cuUpTime = attend.getClockUpTime();
                        LocalDateTime boundaryTime = LocalDateTime.of(localDate, LocalTime.of(8, 30));
                        if(cuUpTime.isBefore(boundaryTime)){
                            cuUpTime = boundaryTime;
                        }
                        Duration between = Duration.between(cuUpTime, attend.getClockDownTime());
                        int length = (int) between.toMinutes() - 9 * 60;
                        attend.setRealTimeLength(length);
                        // 不足30分钟 不计入加班时长
                        int validLength = length - length % 30;
                        attend.setAdditionTimeLength(validLength);
                        attend.setAdditionTimeStr(validLength / 60 + "小时" + validLength % 60 + "分");
                    }
                    msg = "打卡成功！";
                }
                baseMapper.updateById(attend);
            }
            return msg;
        } catch (Exception e) {
            return "打卡异常!";
        }
    }

    /**
     * 获取打卡列表
     *
     * @param userName
     * @param sTime
     * @param eTime
     * @return
     */

    @Override
    public AttendVo getClockList(String userName, LocalDate sTime, LocalDate eTime, Integer page, Integer limit) throws ParseException {
        AttendVo attendVo = new AttendVo();
        LambdaQueryWrapper<Attend> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(userName != null && !userName.isEmpty(), Attend::getName, userName)
                .eq(Attend::getIsDelete,0)
                .between(sTime != null && eTime != null, Attend::getCurrentDateTime, sTime, eTime)
                .orderByDesc(Attend::getCurrentDateTime);
        Page<Attend> attendPage = new Page<>(page, limit);
        attendPage = baseMapper.selectPage(attendPage, wrapper);
        Page<AttendResult> resultPage = new Page<>(page, limit);
        List<AttendResult> list = new ArrayList<>();
        for (Attend record : attendPage.getRecords()) {
            AttendResult attendResult = new AttendResult();
            attendResult.setId(record.getId());
            attendResult.setAdditionTimeStr(record.getAdditionTimeStr());
            attendResult.setClockUpTime(TimeToStr(record.getClockUpTime()));
            attendResult.setClockDownTime(TimeToStr(record.getClockDownTime()));
            attendResult.setCurrentDateTime(record.getCurrentDateTime());
            list.add(attendResult);
        }
        resultPage.setRecords(list);
        resultPage.setTotal(attendPage.getTotal());
        resultPage.setSize(attendPage.getSize());
        resultPage.setCurrent(attendPage.getCurrent());
        attendVo.setAttendList(resultPage);
        LambdaQueryWrapper<Attend> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(userName != null && !userName.isEmpty(), Attend::getName, userName);
        wrapper1.between(Attend::getCurrentDateTime, sTime, eTime);
        wrapper1.eq(Attend::getIsDelete,0);
        List<Attend> attends = baseMapper.selectList(wrapper1);
        if (attends.isEmpty()) {
            return attendVo;
        }
        int monLength = attends.stream().map(Attend::getAdditionTimeLength).reduce(Integer::sum).get();
        attendVo.setMonthTimeDate(monLength / 60 + "小时" + monLength % 60 + "分");
        int weekLength = 0;
        LocalDate now = LocalDate.now();
        //在当前月
        if (now.isBefore(eTime)) {
            LocalDate weekST = now.with(DayOfWeek.MONDAY);
            LocalDate weekET = now.with(DayOfWeek.SUNDAY);
            weekLength = attends.stream().filter(t -> t.getCurrentDateTime().compareTo(weekST) >= 0 && t.getCurrentDateTime().compareTo(weekET) <= 0).mapToInt(Attend::getAdditionTimeLength).sum();
            attendVo.setWeekTimeData(weekLength / 60 + "小时" + weekLength % 60 + "分");
            int restLength = 36 * 60 - monLength;
            if (restLength > 0) {
                restLength = restLength / WorkDayCalculator.countWorkDays(LocalDate.now(), eTime);
                attendVo.setRestAverageTime(restLength / 60 + "小时" + restLength % 60 + "分");
            } else {
                attendVo.setRestAverageTime("0小时0分");
            }
        }
        //计算出勤天数
        attendVo.setAttendDays(attends.size());
        //计算加班天数
        attendVo.setLatterDays((int) attends.stream().filter(t -> t.getClockDownTime() != null && t.getClockDownTime().toLocalTime().isAfter(LocalTime.of(20, 30, 0))).count());
        return attendVo;
    }

    @Override
    public AttendVo getThisWeekClockList(String userName, LocalDate thisWeekMondayLocalDate) {
        AttendVo attendVo = new AttendVo();
        LambdaQueryWrapper<Attend> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.isNotEmpty(userName), Attend::getName, userName)
                .eq(Attend::getIsDelete,0)
                .ge(Attend::getCurrentDateTime, thisWeekMondayLocalDate)
                .orderByDesc(Attend::getCurrentDateTime);

        List<Attend> attendList = this.baseMapper.selectList(wrapper);
        List<AttendResult> list = new ArrayList<>();
        thisWeekMondayLocalDate.getDayOfMonth();
        for (Attend record : attendList) {
            AttendResult attendResult = new AttendResult();
            attendResult.setId(record.getId());
            LocalDate currentDateTime = record.getCurrentDateTime();
            attendResult.setWeek(DateUtil.parseLocalDate2String(currentDateTime, "EEEE"));
            attendResult.setAdditionTimeStr(record.getAdditionTimeStr());
            attendResult.setClockUpTime(TimeToStr(record.getClockUpTime()));
            attendResult.setClockDownTime(TimeToStr(record.getClockDownTime()));
            attendResult.setCurrentDateTime(record.getCurrentDateTime());
            list.add(attendResult);
        }

        Integer weekLength = attendList.stream().mapToInt(Attend::getAdditionTimeLength).sum();
        String weekLengthStr = weekLength / 60 + "小时" + weekLength % 60 + "分";
//        attendVo.setWeekTimeData(weekLengthStr);

        AttendResult attendResult = new AttendResult("总时长", weekLengthStr);
        list.add(attendResult);


        attendVo.setAttendList(new PageDTO<AttendResult>().setRecords(list));
        return attendVo;
    }

    @Override
    public AttendVo getThisMonthClockList(String userName,LocalDate sTime, LocalDate eTime) throws ParseException {
        AttendVo attendVo = new AttendVo();
        LambdaQueryWrapper<Attend> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.isNotEmpty(userName), Attend::getName, userName)
                .eq(Attend::getIsDelete,0)
                .ge(Attend::getCurrentDateTime, sTime)
                .le(Attend::getCurrentDateTime, eTime)
                .orderByDesc(Attend::getCurrentDateTime);

        List<Attend> attendList = this.baseMapper.selectList(wrapper);
        List<AttendResult> list = new ArrayList<>();


        Date lastDay4ThisMonth = DateUtil.getLastDay4ThisMonth();
        System.out.println();
        Map<Integer, Attend> attendListMap = attendList.stream().collect(Collectors.toMap(one -> one.getCurrentDateTime().getDayOfMonth(), one -> one));


        for (int i = 0; i < lastDay4ThisMonth.getDate(); i++) {
            AttendResult attendResult = new AttendResult();
            Attend record = attendListMap.get(i);
            if (ObjectUtils.isNotEmpty(record)) {
                attendResult.setId(record.getId());
                LocalDate currentDateTime = record.getCurrentDateTime();
                attendResult.setWeek(DateUtil.parseLocalDate2String(currentDateTime, "EEEE"));
                attendResult.setAdditionTimeStr(record.getAdditionTimeStr());
                attendResult.setClockUpTime(TimeToStr(record.getClockUpTime()));
                attendResult.setClockDownTime(TimeToStr(record.getClockDownTime()));
                attendResult.setCurrentDateTime(record.getCurrentDateTime());
            }
            list.add(attendResult);
        }

        LambdaQueryWrapper<Attend> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(userName != null && !userName.isEmpty(), Attend::getName, userName);
        wrapper1.between(Attend::getCurrentDateTime, sTime, eTime);
        wrapper1.eq(Attend::getIsDelete,0);
        List<Attend> attends = baseMapper.selectList(wrapper1);
        if (attends.isEmpty()) {
            return attendVo;
        }
        int monLength = attends.stream().map(Attend::getAdditionTimeLength).reduce(Integer::sum).get();
        attendVo.setMonthTimeDate(monLength / 60 + "小时" + monLength % 60 + "分");
        int weekLength = 0;
        LocalDate now = LocalDate.now();
        //在当前月
        if (now.isBefore(eTime)) {
            LocalDate weekST = now.with(DayOfWeek.MONDAY);
            LocalDate weekET = now.with(DayOfWeek.SUNDAY);
            weekLength = attends.stream().filter(t -> t.getCurrentDateTime().compareTo(weekST) >= 0 && t.getCurrentDateTime().compareTo(weekET) <= 0).mapToInt(Attend::getAdditionTimeLength).sum();
            attendVo.setWeekTimeData(weekLength / 60 + "小时" + weekLength % 60 + "分");
        }
        int restLength = 36 * 60 - monLength;
        if (restLength > 0) {
            restLength = restLength / WorkDayCalculator.countWorkDays(LocalDate.now(), eTime);
            attendVo.setRestAverageTime(restLength / 60 + "小时" + restLength % 60 + "分");
        } else {
            attendVo.setRestAverageTime("0小时0分");
        }
        //计算出勤天数
        attendVo.setAttendDays(attends.size());
        //计算加班天数
        attendVo.setLatterDays((int) attends.stream().filter(t -> t.getClockDownTime() != null && t.getClockDownTime().toLocalTime().isAfter(LocalTime.of(20, 30, 0))).count());



        attendVo.setAttendList(new PageDTO<AttendResult>().setRecords(list));
        return attendVo;
    }

    /**
     * 导出excel
     * @param response
     * @param userName
     */
    @Override
    public void exportExcel(HttpServletResponse response, String userName) {
        LambdaQueryWrapper<Attend> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Attend::getName, userName)
                .eq(Attend::getIsDelete,0)
                .orderByDesc(Attend::getCurrentDateTime);
        List<Attend> attends = baseMapper.selectList(wrapper);
        List<AttendExcel> list = new ArrayList<>();
        attends.forEach(item->{
            AttendExcel attendExcel = new AttendExcel();
            attendExcel.setName(item.getName());
            attendExcel.setCurrentDateTime(DateUtil.parseLocalDate2String(item.getCurrentDateTime(), "yyyy-MM-dd"));
            attendExcel.setClockUpTime(TimeToStr(item.getClockUpTime()));
            attendExcel.setClockDownTime(TimeToStr(item.getClockDownTime()));
            attendExcel.setAdditionTimeLength(item.getAdditionTimeLength());
            attendExcel.setAdditionTimeStr(item.getAdditionTimeStr());
            list.add(attendExcel);
        });
        ExcelUtil.exportExcel(response, AttendExcel.class, list, "打卡记录");
    }

    @Override
    public int editClock(Attend attend) {
        Integer id = attend.getId();
        if(attend.getCurrentDateTime()==null){
            attend.setCurrentDateTime(LocalDate.now());
        }
        LocalDateTime clockUpTime = attend.getClockUpTime();
        LocalDateTime clockDownTime = attend.getClockDownTime();
        if(clockDownTime != null && clockUpTime != null) {
            LocalDateTime boundaryTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(8, 30));
            if(clockUpTime.isBefore(boundaryTime)){
                clockUpTime = boundaryTime;
            }
            Duration between = Duration.between(clockUpTime, attend.getClockDownTime());
            int length = (int) between.toMinutes() - 9 * 60;
            attend.setRealTimeLength(length);
            // 不足30分钟 不计入加班时长
            int validLength = length - length % 30;
            attend.setAdditionTimeLength(validLength);
            attend.setAdditionTimeStr(validLength / 60 + "小时" + validLength % 60 + "分");
        }
        if(id==null && attend.getName()!=null){
            LambdaQueryWrapper<Attend> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Attend::getName, attend.getName())
                    .eq(Attend::getCurrentDateTime, attend.getCurrentDateTime())
                    .eq(Attend::getIsDelete,0);
            Long l = baseMapper.selectCount(wrapper);
            if(l>0){
                return 2;
            }
            return baseMapper.insert(attend);
        }else {
            return baseMapper.updateById(attend);
        }
    }

    @Override
    public void deleteClock(List<Integer> ids) {
        if(ids==null || ids.isEmpty()){
            return;
        }
        LambdaUpdateWrapper<Attend> wrapper = new LambdaUpdateWrapper<>();
        wrapper.in(Attend::getId, ids)
                .set(Attend::getIsDelete,1);
        baseMapper.update(null, wrapper);
    }

    @Override
    public List<String> getEstimatedTimeOfDismissal(String userName) {
        LambdaQueryWrapper<Attend> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Attend::getCurrentDateTime, LocalDate.now())
                .eq(userName != null && !userName.isEmpty(), Attend::getName, userName)
                .eq(Attend::getIsDelete,0)
                .last("limit 1");
        Attend attend = baseMapper.selectOne(wrapper);
        if(attend == null || attend.getClockUpTime() == null){
            return null;
        }
        LocalDateTime clockUpTime = attend.getClockUpTime();
        LocalDateTime boundaryTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(8, 30));
        if(clockUpTime.isBefore(boundaryTime)){
            clockUpTime = boundaryTime;
        }
        // 八小时的下班时间
        LocalDateTime realDownTime = clockUpTime.plusHours(9);
        LocalDateTime preDownTime = null;
        // 循环，每次递加半个小时
        while (LocalDateTime.of(LocalDate.now(), LocalTime.of(17, 55, 0)).isAfter(realDownTime)) {
            preDownTime = realDownTime;
            realDownTime = realDownTime.plusMinutes(30);
        }
        return Arrays.asList(preDownTime == null? null : TimeToStr(preDownTime),TimeToStr(realDownTime));

    }

    public String TimeToStr(LocalDateTime localDateTime) {
        if (localDateTime == null) {
            return "未打卡";
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        return localDateTime.format(formatter);
    }

    public LocalTime strToTime(String DateTimeStr) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        return LocalTime.parse(DateTimeStr, formatter);
    }
}
